#ifndef CF40AF7A_60AA_4569_A116_A3A8689E4A72
#define CF40AF7A_60AA_4569_A116_A3A8689E4A72
#include <cmath>
#include <functional>

static double calculate_minimum(std::function<double(double)> const &f,
                                double x1, double dx, double eps) {
  // steo 2 beginning
  while (true) {
    double x2 = x1 + dx;

    double f1 = f(x1);
    double f2 = f(x2);

    double x3;
    if (f1 > f2) {
      x3 = x1 + 2 * dx;
    } else {
      x3 = x1 - dx;
    }

    double f3 = f(x3);

    // step 6
    while (true) {
      double x_min = x1;
      double f_min = f1;

      if (f2 < f_min) {
        f_min = f2;
        x_min = x2;
      }
      if (f3 < f_min) {
        f_min = f3;
        x_min = x3;
      }

      double x_polynom = 0.5 *
                         ((std::pow(x2, 2) - std::pow(x3, 2)) * f1 +
                          (std::pow(x3, 2) - std::pow(x1, 2)) * f2 +
                          (std::pow(x1, 2) - std::pow(x2, 2)) * f3) /
                         ((x2 - x3) * f1 + (x3 - x1) * f2 + (x1 - x2) * f3);
      if (std::isinf(x_polynom)) {
        x1 = x_min;
        break;
      }

      double f_x_polynom = f(x_polynom);

      if (std::fabs((f_min - f_x_polynom) / f_x_polynom) < eps &&
          std::fabs((x_min - x_polynom) / x_polynom) < eps) {
        return x_polynom;
      } else if (x_polynom >= x1 && x_polynom <= x3) {
        if (f_x_polynom < f2) {
          if (x_polynom < x2) {
            x3 = x2;
            x2 = x_polynom;
            f3 = f2;
          } else {
            x1 = x2;
            x2 = x_polynom;
            f1 = f2;
          }
          f2 = f_x_polynom;
        } else {
          if (f_x_polynom < x2) {
            x1 = x_polynom;
            f1 = f_x_polynom;
          } else {
            x3 = x_polynom;
            f3 = f_x_polynom;
          }
        }
      } else {
        x1 = x_polynom;
        break;
      }
    }
  }
}

#endif /* CF40AF7A_60AA_4569_A116_A3A8689E4A72 */
